﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Claims;
using Sinacor.Infra.Common.Security.Authentication.Credentials;
using System.IdentityModel.Tokens;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Sinacor.Infra.Common.Security.Authentication;

namespace Sinacor.Infra.Service.Security.Authorization
{
    /// <summary>
    /// Representa as Claims de Autorização
    /// </summary>
    public class AuthorizationClaims
    {
        /// <summary>
        /// Recupera a Lista de Claims para um token específico
        /// </summary>
        /// <param name="token"></param>
        /// <returns>ClaimSet com as Claims para o Token</returns>
        public static ClaimSet RetrieveTokenClaims(SecurityToken token)
        {
            SinacorToken sinacorToken = token as SinacorToken;

            if (sinacorToken != null)
            {
                UserIdentity identity =
                    new UserIdentity(
                        RetrieveUserId(
                            sinacorToken.UserInfo.UserName), 
                            sinacorToken.UserInfo.UserName,
                            sinacorToken.UserInfo.Password,
                            sinacorToken.UserInfo.TokenId,
                            sinacorToken.UserInfo.CompanyId,
                            RetrieveRequestorIp()
                            );

                //Busca o provider de autorização 
                IAuthorizationProvider authorizationProvider = AuthorizationProviderFactory.RetrieveAuthorizationProvider();
                
                //Retorna a lista de claims pelo provider de Autorização
                IList<Claim> claims = authorizationProvider.RetrieveAuthorizationClaims(sinacorToken);
                
                //Adiciona a claims referente ao Id do usuário
                claims.Add(new Claim(SinacorClaimTypes.Identity.UserId, identity.UserId.ToString(), Rights.PossessProperty));
                //Adiciona a claims referente ao Login do usuário
                claims.Add(new Claim(SinacorClaimTypes.Identity.LoginName, identity.LoginName.ToString(), Rights.PossessProperty));
                //Adiciona a claims referente ao Password cifrado do usuário
                claims.Add(new Claim(SinacorClaimTypes.Identity.Password, identity.Password.ToString(), Rights.PossessProperty));
                //Adiciona a claims referente ao Id do Token
                claims.Add(new Claim(SinacorClaimTypes.Identity.TokenId, identity.Password.ToString(), Rights.PossessProperty));
                //Adiciona a claims referente ao Id da empresa
                claims.Add(new Claim(SinacorClaimTypes.Identity.CompanyId, identity.CompanyId.ToString(), Rights.PossessProperty));
                //Adiciona a claim referente ao Ip do cliente autenticado
                claims.Add(new Claim(SinacorClaimTypes.Identity.ClientHost.Ip, identity.ClientHost.ToString(), Rights.PossessProperty));
                //Adiciona a claim referente ao Ip do cliente autenticado
                claims.Add(new Claim(SinacorClaimTypes.Identity.ClientHost.Name, RetrieveRequestorHostName(), Rights.PossessProperty));

                return new DefaultClaimSet(claims);
            }
            else
                throw new SecurityTokenValidationException("The specified token is not a SinacorToken");
        }

        /// <summary>
        /// Recupera o HostName do Requisitante.
        /// </summary>
        /// <returns></returns>
        private static string RetrieveRequestorHostName()
        {
            return RetrieveHostNameAddressFromMessage();
        }

        /// <summary>
        /// Recupera o código do usuário, a partir do login.
        /// </summary>
        /// <param name="login"></param>
        /// <returns></returns>
        public static int RetrieveUserId(string login)
        {
            AccessControl ac = new AccessControl();
            User user = ac.RetrieveUser(login);
            if (null != user)
                return user.Id;

            throw new Exception("An error ocurred trying to get UserId");
        }

        /// <summary>
        /// Retorna o Ip do requisitante.
        /// </summary>
        /// <returns><see cref="System.Net.IPAddress"/></returns>
        private static string RetrieveRequestorIp()
        {
            return RetrieveIpAddressFromMessage();
        }

        /// <summary>
        /// Recupera o Ip do requisitante pelo contexto da Mensagem
        /// </summary>
        /// <returns></returns>
        private static string RetrieveIpAddressFromMessage()
        {
            string clientHost = "";
            
            OperationContext operation = OperationContext.Current;
            
            if (operation != null)
            {
                MessageHeaders headers = operation.IncomingMessageHeaders;
                int headerIndex = headers.FindHeader(AuthenticationHeader.Name, AuthenticationHeader.NameSpace);
                
                if (headerIndex >= 0)
                {
                    AuthenticationHeader header = headers.GetHeader<AuthenticationHeader>(headerIndex);
                    if (header != null)
                        if (string.IsNullOrEmpty(header.ClientIp))
                            clientHost = header.ClientIp;
                        else
                            clientHost = header.ClientHostName;
                }
            }

            return clientHost;
        }

        /// <summary>
        /// Recupera o HostName do requisitante pelo contexto da Mensagem
        /// </summary>
        /// <returns></returns>
        private static string RetrieveHostNameAddressFromMessage()
        {
            string clientHost = "";

            OperationContext operation = OperationContext.Current;

            if (operation != null)
            {
                MessageHeaders headers = operation.IncomingMessageHeaders;
                int headerIndex = headers.FindHeader(AuthenticationHeader.Name, AuthenticationHeader.NameSpace);

                if (headerIndex >= 0)
                {
                    AuthenticationHeader header = headers.GetHeader<AuthenticationHeader>(headerIndex);
                    if (header != null)
                        clientHost = header.ClientHostName;
                }
            }

            return clientHost;
        }
        
    }
}
